'use client'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { Heart, MessageCircle, Share2, BadgeCheck, MoreHorizontal, Bookmark, Flag, Link as LinkIcon, Trash2, ArrowLeft } from 'lucide-react'; import { fetchApi } from '@/lib/utils/client'; import useAuth from '@/hooks/useAuth'; import { formatDate, getDateTime } from '@/lib/utils/client'; import { FeedPostDetail, FeedReply } from '@/types/feed/post'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from '@/components/ui/dropdown-menu'; import TagChip from '../../../_component/TagChip'; import FeedLightbox from '../../../_component/FeedLightbox'; import FeedReplyList from './FeedReplyList'; import FeedReplyComposer from './FeedReplyComposer'; type Props = { post: FeedPostDetail; initialReplies: FeedReply[]; initialRepliesTotal: number; }; export default function FeedPostView({ post, initialReplies, initialRepliesTotal }: Props) { const router = useRouter(); const { loginCheck } = useAuth(); const [likes, setLikes] = useState(post.likes); const [liked, setLiked] = useState(post.hasLike); const [bookmarked, setBookmarked] = useState(post.hasBookmark); const [busy, setBusy] = useState(false); const [replyTarget, setReplyTarget] = useState(null); const [refreshKey, setRefreshKey] = useState(0); const [lightboxIndex, setLightboxIndex] = useState(null); const authorDisplay = post.authorName || post.authorSID || '알 수 없음'; const avatarInitial = (authorDisplay.charAt(0) || '?').toUpperCase(); const imageUrls = post.images.map(i => i.url); const handleLike = async () => { if (!loginCheck() || busy) { return; } setBusy(true); try { const res = await fetchApi<{ hasLike: boolean; likes: number }>(`/api/feed/post/${post.postID}/like`, { method: 'POST', silent: true }); if (res.success && res.data) { setLiked(res.data.hasLike); setLikes(res.data.likes); } } catch (err) { console.error(err); } finally { setBusy(false); } }; const handleBookmark = async () => { if (!loginCheck() || busy) { return; } setBusy(true); try { const res = await fetchApi<{ hasBookmark: boolean; bookmarks: number }>(`/api/feed/post/${post.postID}/bookmark`, { method: 'POST', silent: true }); if (res.success && res.data) { setBookmarked(res.data.hasBookmark); } } catch (err) { console.error(err); } finally { setBusy(false); } }; const handleShare = async () => { const url = `${window.location.origin}/feed/post/${post.postID}`; try { await navigator.clipboard.writeText(url); alert('링크가 복사되었습니다.'); } catch { prompt('링크를 복사하세요:', url); } }; const handleDelete = async () => { if (!confirm('이 게시글을 삭제할까요?')) { return; } try { const res = await fetchApi(`/api/feed/post/${post.postID}`, { method: 'DELETE', silent: true }); if (res.success) { router.push('/feed/all'); router.refresh(); } } catch (err) { console.error(err); } }; const handleReplyTarget = (target: FeedReply) => { setReplyTarget(target); }; const handleReplySubmitted = () => { setRefreshKey((k) => k + 1); }; const handleBack = () => { if (window.history.length > 1) { router.back(); } else { router.push('/feed/all'); } }; return ( <>
{post.authorSID ? ( {post.authorThumb ? ( {authorDisplay} ) : ( {avatarInitial} )} ) : ( {avatarInitial} )}
{post.authorSID ? ( {authorDisplay} ) : ( {authorDisplay} )} {post.isCreator && ( )}
{formatDate(post.createdAt)} · 조회 {post.views.toLocaleString()}
{bookmarked ? '저장 해제' : '저장'} 링크 복사 {post.isOwner && ( <> 삭제 )} {!post.isOwner && ( 신고 )}

{post.content}

{post.images.length > 0 && (
{post.images.slice(0, 4).map((image, index) => ( ))}
)} {post.tags.length > 0 && (
{post.tags.map((tag) => ( ))}
)}
{post.comments.toLocaleString()}
setReplyTarget(null)} onSubmitted={handleReplySubmitted} /> {lightboxIndex !== null && ( setLightboxIndex(null)} /> )} ); }